/*
 * Copyright 2008-2023 dexian.vip. All rights reserved.
 * Support: http://www.dexian.vip
 * License: http://www.dexian.vip/license
 */

package vip.dexian.code.config;

import vip.dexian.core.base.bean.BaseEntity;
import vip.dexian.core.base.bean.OrderEntity;
import vip.dexian.core.base.bean.UniqueEntity;
import vip.dexian.core.base.bean.UniqueOrderEntity;
import jakarta.persistence.Column;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.Lob;
import jakarta.validation.constraints.DecimalMax;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Digits;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Positive;
import jakarta.validation.constraints.PositiveOrZero;
import jakarta.validation.constraints.Size;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.annotations.Comment;
import org.hibernate.cfg.ImprovedNamingStrategy;
import org.hibernate.validator.constraints.Length;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 自定义java bean field
 *
 * @author 挺好的 2023年06月06日 17:18
 */
@Getter
@Setter
public class CustomField {

    public CustomField () {
    }

    public CustomField (Field field, boolean isForm) {
        Comment annotation = field.getAnnotation(Comment.class);

        if (annotation == null) {
            return;
        }

        this.comment = annotation.value();

        this.name = field.getName();

        Class superClass = field.getType().getSuperclass();

        if (superClass == BaseEntity.class || superClass == OrderEntity.class || superClass == UniqueEntity.class || superClass == UniqueOrderEntity.class

        ) {
            this.isEntity = true;
        } else {
            this.isEntity = false;
        }

        JoinColumn joinColumn = field.getAnnotation(JoinColumn.class);

        if (joinColumn != null) {
            this.columnName = joinColumn.name();
        } else {
            this.columnName = ImprovedNamingStrategy.INSTANCE.columnName(this.name);
        }

        this.javaType = field.getType().getSimpleName();

        Column column = field.getAnnotation(Column.class);

        if (column != null) {
            this.length    = column.length();
            this.nullable  = column.nullable();
            this.unique    = column.unique();
            this.precision = column.precision();
            this.scale     = column.scale();
        }

        this.bool = field.getType() == Boolean.class;

        this.jdbcType = this.getJdbcType(field, column);

        this.annotations = new ArrayList <>();

        Annotation[] annotations = field.getDeclaredAnnotations();

        // 加入校验的约束
        for (Annotation constraintAnnotation : annotations) {

            String annotationValue = new AnnotationValue(constraintAnnotation).toString();

            if (StringUtils.isEmpty(annotationValue)) {
                continue;
            }

            this.annotations.add(annotationValue);
        }
    }

    public CustomField (Field field) {
        this(field, true);
    }

    @Override
    public String toString () {
        return this.name + "," + this.columnName + "," + this.jdbcType;
    }

    /**
     * 是否是entity
     */
    private Boolean isEntity;

    /**
     * 属性名称
     */
    private String name;

    /**
     * 列名称
     */
    private String columnName;

    /**
     * 属性java类型
     */
    private String javaType;

    /**
     * 属性注释
     */
    private String comment;

    /**
     * 数据库对应的类型。 包括字段的长度（String），精度（Number）
     */
    private String jdbcType;

    /**
     * 长度
     */
    private int length;

    /**
     * 是否是布尔值
     */
    private boolean bool;

    /**
     * 是否为空
     */
    private boolean nullable;

    /**
     * 是否唯一
     */
    private boolean unique;

    /**
     * 数字的有效位数。包含小数
     */
    private int precision;

    /**
     * 小数的有效位数。保留多少位
     */
    private int scale;

    /**
     * 注解
     */
    private List <String> annotations;

    @Getter
    @Setter
    static class AnnotationValue {

        /**
         * 注解
         */
        private Annotation annotation;

        @Override
        public String toString () {
            return this.constraintName;
        }

        public AnnotationValue (Annotation annotation) {
            this.annotation = annotation;
            this.init();
        }

        /**
         * 初始化字段的注解
         */
        public void init () {
            if (this.annotation instanceof NotEmpty) {
                NotEmpty constraint = (NotEmpty) this.annotation;

                this.simpleConstraintName = "@NotEmpty";

                this.message = constraint.message();
                this.groups  = constraint.groups();


            } else if (this.annotation instanceof NotNull) {
                NotNull constraint = (NotNull) this.annotation;

                this.simpleConstraintName = "@NotNull";
                this.message              = constraint.message();
                this.groups               = constraint.groups();


            } else if (this.annotation instanceof NotBlank) {

                NotBlank constraint = (NotBlank) this.annotation;

                this.simpleConstraintName = "@NotBlank";
                this.message              = constraint.message();
                this.groups               = constraint.groups();


            } else if (this.annotation instanceof Email) {

                Email constraint = (Email) this.annotation;

                this.simpleConstraintName = "@Email";
                this.message              = constraint.message();
                this.groups               = constraint.groups();


            } else if (this.annotation instanceof Length) {
                Length constraint = (Length) this.annotation;

                this.simpleConstraintName = "@Length";
                this.message              = constraint.message();
                this.groups               = constraint.groups();

                if (constraint.min() != 0) {
                    this.minLength = constraint.min();
                }

                if (constraint.max() != Integer.MAX_VALUE) {
                    this.maxLength = constraint.max();
                }


            } else if (this.annotation instanceof Min) {
                Min constraint = (Min) this.annotation;

                this.simpleConstraintName = "@Min";
                this.message              = constraint.message();
                this.groups               = constraint.groups();
                this.min                  = constraint.value();


            } else if (this.annotation instanceof Max) {
                Max constraint = (Max) this.annotation;

                this.simpleConstraintName = "@Max";
                this.message              = constraint.message();
                this.groups               = constraint.groups();
                this.max                  = constraint.value();


            } else if (this.annotation instanceof Pattern) {

                Pattern constraint = (Pattern) this.annotation;

                this.simpleConstraintName = "@Pattern";
                this.message              = constraint.message();
                this.groups               = constraint.groups();
                this.regexp               = constraint.regexp();


            } else if (this.annotation instanceof Positive) {

                Positive constraint = (Positive) this.annotation;

                this.simpleConstraintName = "@Positive";
                this.message              = constraint.message();
                this.groups               = constraint.groups();


            } else if (this.annotation instanceof PositiveOrZero) {

                PositiveOrZero constraint = (PositiveOrZero) this.annotation;

                this.simpleConstraintName = "@PositiveOrZero";
                this.message              = constraint.message();
                this.groups               = constraint.groups();


            } else if (this.annotation instanceof Digits) {

                Digits constraint = (Digits) this.annotation;

                this.simpleConstraintName = "@Digits";
                this.message              = constraint.message();
                this.groups               = constraint.groups();

                this.integer  = constraint.integer();
                this.fraction = constraint.fraction();


            } else if (this.annotation instanceof DecimalMax) {

                DecimalMax constraint = (DecimalMax) this.annotation;

                this.simpleConstraintName = "@DecimalMax";
                this.message              = constraint.message();
                this.groups               = constraint.groups();

                this.value     = constraint.value();
                this.inclusive = constraint.inclusive();


            } else if (this.annotation instanceof DecimalMin) {

                DecimalMin constraint = (DecimalMin) this.annotation;

                this.simpleConstraintName = "@DecimalMin";
                this.message              = constraint.message();
                this.groups               = constraint.groups();

                this.value     = constraint.value();
                this.inclusive = constraint.inclusive();

            } else if (this.annotation instanceof Size) {

                Size constraint = (Size) this.annotation;

                this.simpleConstraintName = "@Size";
                this.message              = constraint.message();
                this.groups               = constraint.groups();
                this.min                  = (long) constraint.min();
                this.max                  = (long) constraint.max();
            }

            this.constraintName = this.build();
        }

        /**
         * 处理分组并且导包
         *
         * @param groups
         *         分组信息
         *
         * @return
         */
        private String process (Class[] groups) {

            if (ArrayUtils.isEmpty(groups)) {
                return StringUtils.EMPTY;
            }

            StringBuilder builder = new StringBuilder();

            builder.append(", groups = {");

            for (int i = 0; i < groups.length; i++) {
                Class group = groups[i];
                builder.append(group.getSimpleName() + ".class");

                if (i < groups.length - 1) {
                    builder.append(", ");
                }
            }

            builder.append("}");

            return builder.toString();
        }

        /**
         * @return
         */
        private String build () {

            if (StringUtils.isEmpty(this.simpleConstraintName)) {
                return StringUtils.EMPTY;
            }

            StringBuilder builder = new StringBuilder(this.simpleConstraintName);

            builder.append(" (");

            if (StringUtils.isNotEmpty(this.message)) {
                builder.append("message = \"");
                builder.append(this.message);
                builder.append("\"");
            }

            String group = this.process(this.groups);

            if (StringUtils.isNotEmpty(group)) {
                builder.append(group);
            }

            if (this.min != null) {
                builder.append(", min = " + this.min + "");
            }

            if (this.max != null) {
                builder.append(", max = " + this.max + "");
            }

            if (StringUtils.isNotEmpty(this.regexp)) {
                builder.append(", regexp = \"");
                builder.append(this.regexp);
                builder.append("\"");
            }

            if (this.minLength != null) {
                builder.append(", min = " + this.minLength + "");
            }

            if (this.maxLength != null) {
                builder.append(", max = " + this.maxLength + "");
            }

            if (StringUtils.isNotEmpty(this.value)) {
                builder.append(", value = \"");
                builder.append(this.value);
                builder.append("\"");
            }

            if (this.inclusive != null) {
                builder.append(", inclusive = " + this.inclusive + "");
            }

            if (this.integer != null && this.integer > 0) {
                builder.append(", integer = " + this.integer + "");
            }

            if (this.fraction != null && this.fraction > 0) {
                builder.append(", fraction = " + this.fraction + "");
            }

            builder.append(")");

            return builder.toString();
        }

        /**
         * 约束的简单名称
         */
        private String simpleConstraintName;

        /**
         * 约束条件，完整的，包含了消息等
         */
        private String constraintName;

        /**
         * 消息
         */
        private String message;

        /**
         * 分组
         */
        private Class[] groups;

        /**
         * 最小值
         */
        private Long min;

        /**
         * 最大值
         */
        private Long max;

        /**
         * 正则
         */
        private String regexp;

        /**
         * 最小长度
         */
        private Integer minLength;

        /**
         * 最大长度
         */
        private Integer maxLength;

        /**
         * 整数位数
         */
        private Integer integer;

        /**
         * 小数位数
         */
        private Integer fraction;

        /**
         * 用于 DecimalMin / DecimalMax
         */
        private String value;

        /**
         * 是否包含
         */
        private Boolean inclusive;
    }

    /**
     * 获取jdbc类型
     *
     * @param field
     *         属性
     *
     * @return
     */
    private String getJdbcType (Field field, Column column) {

        if (column == null) {
            return "VARCHAR";
        }

        Class clazz = field.getType();

        String columnDefinition = column.columnDefinition();

        if (StringUtils.isNotEmpty(columnDefinition) && (columnDefinition.contains("text") || columnDefinition.contains(
                "TEXT"))) {
            return "TEXT";
        }

        Lob lob = field.getAnnotation(Lob.class);

        if (lob != null && clazz == String.class) {
            return "LONGTEXT";
        }

        // 如果是枚举
        if (clazz.isEnum()) {
            return "TINYINT(1) ";
        }

        if (clazz == Boolean.class) {
            return "BIT(1)";
        }

        if (clazz == Byte.class) {
            return "TINYINT(2) UNSIGNED";
        }

        if (clazz == Short.class) {
            if (this.precision == 0) {
                this.precision = 6;
            }
            return "SMALLINT(" + this.precision + ") UNSIGNED";
        }

        if (clazz == Integer.class) {
            if (this.precision == 0) {
                this.precision = 11;
            }
            return "INT(" + this.precision + ") UNSIGNED";
        }

        if (clazz == Long.class) {
            if (this.precision == 0) {
                this.precision = 20;
            }
            return "BIGINT(" + this.precision + ") UNSIGNED";
        }

        if (clazz == Float.class) {
            if (this.precision == 0) {
                this.precision = 11;
            }
            return "FLOAT(" + this.precision + "," + this.scale + ")";
        }

        if (clazz == Double.class) {
            return "DOUBLE(" + this.precision + "," + this.scale + ")";
        }

        if (clazz == Date.class) {
            return "DATETIME";
        }

        if (clazz == LocalDateTime.class) {
            return "DATETIME";
        }

        if (clazz == BigDecimal.class) {
            return "DECIMAL(" + this.precision + "," + this.scale + ")";
        }

        if (clazz == String.class) {
            return "VARCHAR(" + this.length + ")";
        }

        if (clazz.getSuperclass() == BaseEntity.class || clazz.getSuperclass() == OrderEntity.class) {
            return "BIGINT(20)";
        }

        return "VARCHAR(" + this.length + ")";
    }
}
